"""
群分组管理插件

命令：
- group                          显示帮助
- group ls                        列出所有分组
- group create 组名               创建新分组
- group add QQ号/@用户 组名 [昵称] 将 QQ 用户加入分组（可指定昵称）
- group show 组名                 显示指定分组的所有成员
- group rm 组名                   删除分组（同时删除该分组下的所有成员）
- group rm QQ号/@用户 [组名]      删除成员（指定组名则只删除该组，否则删除所有组）
"""
from typing import List, Optional

from nonebot import on_command
from nonebot.adapters.onebot.v11 import (
    ActionFailed,
    Bot,
    Message,
    MessageEvent,
)
from nonebot.exception import FinishedException
from nonebot.log import logger
from nonebot.params import CommandArg

from src.common.django_db import (
    add_member_to_group,
    create_group,
    delete_group,
    get_group_by_name,
    get_group_members,
    list_groups,
    remove_member_from_group,
)


HELP_TEXT = (
    "group 命令可用操作：\n"
    "1. group ls                         查看所有分组\n"
    "2. group create 组名                创建新的分组\n"
    "3. group add QQ号/@用户 组名 [昵称] 将 QQ 用户加入指定分组（可指定昵称）\n"
    "4. group show 组名                  显示指定分组的所有成员\n"
    "5. group rm 组名                    删除分组（同时删除该分组下的所有成员）\n"
    "6. group rm QQ号/@用户 [组名]       删除成员（指定组名则只删除该组，否则删除所有组）\n"
)


group_command = on_command("group", priority=10, block=True)


@group_command.handle()
async def handle_group_command(
    _bot: Bot,
    _event: MessageEvent,
    arg: Message = CommandArg(),
):
    try:
        text = arg.extract_plain_text().strip()
        if not text:
            await group_command.finish(HELP_TEXT)

        parts = text.split()
        action = parts[0].lower()

        if action == "ls":
            await _handle_list_groups()
        elif action == "create":
            await _handle_create_group(parts)
        elif action == "add":
            await _handle_add_member(_bot, _event, parts, arg)
        elif action == "show":
            await _handle_show_group(parts)
        elif action == "rm":
            await _handle_rm(_bot, _event, parts, arg)
        else:
            await group_command.finish(HELP_TEXT)

    except FinishedException:
        raise
    except Exception as exc:  # pragma: no cover - 运行时兜底
        logger.exception("处理 group 命令时出现异常")
        await group_command.finish(f"执行失败：{exc}")


async def _handle_list_groups():
    groups = await list_groups()
    if not groups:
        await group_command.finish("暂无分组，可使用 `group create 分组名` 创建。")
        return

    lines = ["当前分组："]
    for g in groups:
        desc = f" - {g.description}" if g.description else ""
        lines.append(f"- {g.name}{desc}".rstrip())
    await group_command.finish("\n".join(lines))


async def _handle_show_group(parts: List[str]):
    """显示指定分组的所有成员"""
    if len(parts) < 2:
        await group_command.finish("用法：group show 组名")
        return
    
    group_name = parts[1]
    members = await get_group_members(group_name)
    
    if not members:
        # 检查分组是否存在
        group = await get_group_by_name(group_name)
        if not group:
            await group_command.finish(f"分组 `{group_name}` 不存在。")
        else:
            await group_command.finish(f"分组 `{group_name}` 中暂无成员。")
        return
    
    # 格式化输出成员列表
    lines = [f"分组 `{group_name}` 的成员（共 {len(members)} 人）："]
    for i, member in enumerate(members, 1):
        if member.qq_nickname:
            lines.append(f"{i}. {member.qq_nickname} (QQ {member.qq_id})")
        else:
            lines.append(f"{i}. QQ {member.qq_id}")
    
    await group_command.finish("\n".join(lines))


async def _handle_create_group(parts: List[str]):
    if len(parts) < 2:
        await group_command.finish("用法：group create 组名")
        return

    name = parts[1]
    group, created = await create_group(name=name)
    if created:
        await group_command.finish(f"已创建分组：{group.name}")
    else:
        await group_command.finish(f"分组 `{group.name}` 已存在。")


async def _handle_add_member(bot: Bot, event: MessageEvent, parts: List[str], arg: Message):
    """
    添加成员：
    - group add QQ号 组名 [昵称]
    - group add @用户 组名 [昵称]
    - group add @用户1 @用户2 组名 [昵称1 昵称2 ...]（昵称按顺序对应，不够则自动获取）
    """
    # 优先从命令参数中提取所有 @ 的 QQ 号
    qq_list = _extract_all_at_qq(arg)
    if not qq_list:
        # 兜底：从完整消息中再尝试一次
        qq_list = _extract_all_at_qq(event.message)

    # ========== 情况一：命令中包含一个或多个 @ ==========
    if qq_list:
        # 使用 extract_plain_text() 提取纯文本，然后按空格分割
        # @ 消息段会被过滤掉，所以文本会是 "add 组名 昵称1 昵称2 ..."
        arg_text = arg.extract_plain_text().strip()
        arg_parts = arg_text.split() if arg_text else []

        # 跳过 "add" 命令名
        if len(arg_parts) < 2:
            await group_command.finish(
                "用法：group add @用户 组名 [昵称 ...]\n"
                "提示：请确保在 @ 用户后输入组名，组名和昵称之间用空格分隔（昵称可选）。"
            )
            return

        group_name = arg_parts[1]
        nickname_parts = arg_parts[2:]  # 昵称列表，可为空

        # 检查分组是否存在
        group = await get_group_by_name(group_name)
        if not group:
            groups = await list_groups()
            if groups:
                group_names = "、".join([g.name for g in groups])
                await group_command.finish(
                    f"分组 `{group_name}` 不存在。\n"
                    f"现有分组：{group_names}\n"
                    f"可使用 `group create {group_name}` 创建新分组。"
                )
            else:
                await group_command.finish(
                    f"分组 `{group_name}` 不存在，且当前没有任何分组。\n"
                    f"请先使用 `group create {group_name}` 创建分组。"
                )
            return

        # 逐个处理 @ 用户
        lines: List[str] = []
        for idx, qq_id in enumerate(qq_list, start=1):
            nickname_value = nickname_parts[idx - 1] if idx - 1 < len(nickname_parts) else ""
            if not nickname_value:
                nickname_value = await _resolve_member_name(
                    bot, qq_id, getattr(event, "group_id", None)
                )

            _, created = await add_member_to_group(
                group, qq_id=qq_id, qq_nickname=nickname_value
            )
            status = "已新增" if created else "已存在"
            nickname_info = f"（{nickname_value}）" if nickname_value else ""
            lines.append(f"{idx}. QQ {qq_id}{nickname_info} - {status}")

        header = f"分组 `{group.name}` 添加结果（共 {len(qq_list)} 人）："
        await group_command.finish("\n".join([header] + lines))
        return

    # ========== 情况二：没有 @，使用 QQ 号 ==========
    if len(parts) < 3:
        await group_command.finish("用法：group add QQ号/@用户 组名 [昵称]")
        return

    qq_raw = parts[1]
    group_name = parts[2]
    nickname_value = parts[3] if len(parts) > 3 else ""

    # 如果 parts[1] 看起来像 @ 文本，但没有真正的 @ 段
    if qq_raw.startswith("@"):
        await group_command.finish("请直接 @ 用户，而不是输入 @ 的文本形式。\n用法：group add @用户 组名")
        return

    try:
        qq_id = int(qq_raw)
    except ValueError:
        await group_command.finish("用户参数必须是 QQ 号（纯数字）或 @ 用户。\n用法：group add QQ号/@用户 组名")
        return

    # 检查分组是否存在
    group = await get_group_by_name(group_name)
    if not group:
        groups = await list_groups()
        if groups:
            group_names = "、".join([g.name for g in groups])
            await group_command.finish(
                f"分组 `{group_name}` 不存在。\n"
                f"现有分组：{group_names}\n"
                f"可使用 `group create {group_name}` 创建新分组。"
            )
        else:
            await group_command.finish(
                f"分组 `{group_name}` 不存在，且当前没有任何分组。\n"
                f"请先使用 `group create {group_name}` 创建分组。"
            )
        return

    if not nickname_value:
        nickname_value = await _resolve_member_name(bot, qq_id, getattr(event, "group_id", None))

    _, created = await add_member_to_group(group, qq_id=qq_id, qq_nickname=nickname_value)
    if created:
        nickname_info = f"（{nickname_value}）" if nickname_value else ""
        await group_command.finish(f"已将 QQ {qq_id}{nickname_info} 添加到 `{group.name}` 分组。")
    else:
        await group_command.finish(f"QQ {qq_id} 已在 `{group.name}` 分组中。")


async def _handle_rm(bot: Bot, event: MessageEvent, parts: List[str], arg: Message):
    """
    统一的删除命令处理
    - group rm 组名                    -> 删除分组
    - group rm QQ号/@用户              -> 删除成员（所有组）
    - group rm QQ号/@用户 组名         -> 删除成员（指定组）
    """
    # 优先尝试从命令参数中提取 @ 用户的 QQ 号
    # 调试：先打印 arg 的完整信息
    logger.debug(f"arg 完整内容: {arg}, 消息段数量: {len(arg)}")
    for i, seg in enumerate(arg):
        logger.debug(f"arg[{i}]: type={seg.type}, data={seg.data}, str={str(seg)}")
    
    qq_id = _extract_at_qq(arg)
    
    # 调试：检查 arg 中的消息段
    if not qq_id:
        logger.debug(f"从 arg 中未提取到 @ 用户，arg 消息段类型: {[seg.type for seg in arg]}")
        for seg in arg:
            if seg.type == "at":
                logger.debug(f"发现 @ 消息段，但未提取到 QQ 号，数据: {seg.data}, 数据类型: {type(seg.data)}")
    
    # 如果从 arg 中没提取到，尝试从完整消息中提取
    if not qq_id:
        qq_id = _extract_at_qq(event.message)
        if qq_id:
            logger.debug(f"从完整消息中提取到 @ 用户 QQ: {qq_id}")
        else:
            logger.debug(f"从完整消息中也未提取到 @ 用户，完整消息段: {[seg.type for seg in event.message]}")
    
    # 如果没有 @ 消息段，检查参数数量
    if not qq_id and len(parts) < 2:
        await group_command.finish("用法：group rm 组名 或 group rm QQ号/@用户 [组名]")
        return
    
    # 判断是删除组还是删除成员
    if qq_id:
        # 有 @ 消息段，肯定是删除成员
        # 格式是 rm @用户 [组名]
        arg_text = arg.extract_plain_text().strip()
        arg_parts = arg_text.split() if arg_text else []
        
        # arg_parts[0] 是 "rm"，arg_parts[1] 可能是组名（如果有）
        group_name = arg_parts[1] if len(arg_parts) > 1 else None
        
        # 删除成员
        deleted_count = await remove_member_from_group(qq_id, group_name)
        
        if deleted_count > 0:
            if group_name:
                await group_command.finish(f"已从分组 `{group_name}` 中删除 QQ {qq_id}。")
            else:
                await group_command.finish(f"已删除 QQ {qq_id} 在所有分组中的信息（共 {deleted_count} 条记录）。")
        else:
            if group_name:
                await group_command.finish(f"QQ {qq_id} 不在分组 `{group_name}` 中。")
            else:
                await group_command.finish(f"QQ {qq_id} 不在任何分组中。")
    else:
        # 没有 @ 消息段，需要判断是 QQ 号还是组名
        first_param = parts[1]
        
        # 检查是否是 @ 的文本形式（但没被识别为 @ 消息段）
        if first_param.startswith("@"):
            await group_command.finish(
                "无法识别 @ 用户，请确保直接 @ 用户而不是输入 @ 的文本形式。\n"
                "用法：group rm @用户 [组名]"
            )
            return
        
        # 尝试解析为数字（QQ 号）
        try:
            qq_id = int(first_param)
            # 是 QQ 号，删除成员
            group_name = parts[2] if len(parts) > 2 else None
            
            deleted_count = await remove_member_from_group(qq_id, group_name)
            
            if deleted_count > 0:
                if group_name:
                    await group_command.finish(f"已从分组 `{group_name}` 中删除 QQ {qq_id}。")
                else:
                    await group_command.finish(f"已删除 QQ {qq_id} 在所有分组中的信息（共 {deleted_count} 条记录）。")
            else:
                if group_name:
                    await group_command.finish(f"QQ {qq_id} 不在分组 `{group_name}` 中。")
                else:
                    await group_command.finish(f"QQ {qq_id} 不在任何分组中。")
        except ValueError:
            # 不是数字，认为是组名，删除分组
            group_name = first_param
            success = await delete_group(group_name)
            
            if success:
                await group_command.finish(f"已删除分组 `{group_name}` 及其所有成员。")
            else:
                await group_command.finish(f"分组 `{group_name}` 不存在。")


def _extract_all_at_qq(message: Message) -> List[int]:
    """
    从消息中提取所有 @ 用户的 QQ 号列表，按顺序返回。
    在 OneBot V11 中，@ 消息段可能使用以下字段：
    - qq: 旧版本使用的字段
    - user_id: 标准字段
    - target: 某些实现使用的字段
    """
    qq_list: List[int] = []
    for segment in message:
        if segment.type == "at":
            logger.debug(f"发现 @ 消息段，完整数据: {segment.data}")
            qq = (
                segment.data.get("qq")
                or segment.data.get("user_id")
                or segment.data.get("target")
            )
            if qq:
                try:
                    qq_id = int(qq)
                    logger.debug(f"从消息段中提取到 @ 用户 QQ: {qq_id}, 使用的字段: {segment.data}")
                    qq_list.append(qq_id)
                except (ValueError, TypeError):
                    logger.debug(
                        f"无法将 @ 消息段数据转换为整数: {qq}, 类型: {type(qq)}, 完整数据: {segment.data}"
                    )
                    continue
            else:
                logger.debug(
                    f"@ 消息段中没有找到 qq/user_id/target 字段，可用键: {list(segment.data.keys())}, 完整数据: {segment.data}"
                )
                if segment.data:
                    for key, value in segment.data.items():
                        if value and isinstance(value, (int, str)):
                            try:
                                qq_id = int(value)
                                logger.debug(f"从字段 {key} 中提取到 QQ 号: {qq_id}")
                                qq_list.append(qq_id)
                                break
                            except (ValueError, TypeError):
                                continue
    return qq_list


def _extract_at_qq(message: Message) -> Optional[int]:
    """
    从消息中提取第一个 @ 用户的 QQ 号
    """
    qq_list = _extract_all_at_qq(message)
    return qq_list[0] if qq_list else None


async def _resolve_member_name(bot: Bot, qq_id: int, group_id: Optional[int]) -> str:
    """
    未提供昵称时，通过 API 依次尝试获取：好友备注 > 群名片 > QQ 昵称
    """
    remark = await _get_friend_remark(bot, qq_id)
    if remark:
        return remark

    card = await _get_group_display(bot, qq_id, group_id)
    if card:
        return card

    nickname = await _get_stranger_nickname(bot, qq_id)
    return nickname or ""


async def _get_friend_remark(bot: Bot, qq_id: int) -> str:
    try:
        friends = await bot.get_friend_list()
    except ActionFailed:
        return ""

    for friend in friends or []:
        if friend.get("user_id") == qq_id:
            return friend.get("remark") or ""
    return ""


async def _get_group_display(bot: Bot, qq_id: int, group_id: Optional[int]) -> str:
    if not group_id:
        return ""

    try:
        info = await bot.get_group_member_info(
            group_id=group_id,
            user_id=qq_id,
            no_cache=True,
        )
    except ActionFailed:
        return ""

    return info.get("card") or info.get("nickname") or ""


async def _get_stranger_nickname(bot: Bot, qq_id: int) -> str:
    try:
        info = await bot.get_stranger_info(user_id=qq_id, no_cache=True)
    except ActionFailed:
        return ""

    return info.get("nickname") or ""

